#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <pthread.h>

struct job {
    struct job *j_next;
    struct job *j_prev;
    pthread_t j_id; /* tells which thread handles this job */
};

struct queue {
    struct job *q_head;
    struct job *q_tail;
    pthread_rwlock_t q_lock;
};

int queue_init(struct queue *qp) {
    int err;
    qp->q_head= NULL;
    qp->q_tail= NULL;
    if ((err= pthread_rwlock_init(&qp->q_lock, NULL)) != 0)
        return err;
    /* ... continue initialization ... */
    return 0;
}

/* insert job at the head of queue*/
void job_insert(struct queue *qp, struct job *jp) {
    pthread_rwlock_wrlock(&qp->q_lock);
    jp->j_next= qp->q_head;
    jp->j_prev= NULL;
    if (qp->q_head != NULL)
        qp->q_head->j_prev= jp;
    else 
        qp->q_tail= jp; /* is is empty */
    qp->q_head= jp; 
    pthread_rwlock_unlock(&qp->q_lock);
}

/* Append a job on the tail of the queue */
void job_append(struct queue *qp, struct job *jp) {
    pthread_rwlock_wrlock(&qp->q_lock);
    jp->j_next= NULL;
    jp->j_prev= qp->q_tail;
    if (qp->q_tail != NULL) 
        qp->q_tail->j_next= jp;
    else
        qp->q_head= jp;
    qp->q_tail= jp;
    pthread_rwlock_unlock(&qp->q_lock);
}

void job_remove(struct queue *qp, struct job *jp) {
    pthread_rwlock_wrlock(&qp->q_lock);
    //if (jp == qp->q_head) {
    //    qp->q_head= jp->j_next;
    //    if (qp->q_tail== qp->q_head)
    //        qp->q_tail= NULL;
    //    else 
    //        jp->j_next->j_prev= NULL;

    //} else if (jp == qp->q_tail) {
    //    jp->j_prev->j_next= NULL;
    //    qp->q_tail= jp->j_prev;

    //} else {
    //    jp->j_prev->j_next= jp->j_next;
    //    jp->j_next->j_prev= jp->j_prev;
    //}
    // or write next way

    if (jp->j_next)
        jp->j_next->j_prev= jp->j_prev;  
    if (jp->j_prev)
        jp->j_prev->j_next= jp->j_next;
    if (jp == qp->q_head)
        qp->q_head= jp->j_next;
    if (jp == qp->q_tail)
        qp->q_tail= jp->j_prev;
    pthread_rwlock_unlock(&qp->q_lock);
}

struct job * job_find(struct queue *qp, int id) {
    if (pthread_rwlock_rdlock(&qp->q_lock))
        return NULL;
    struct job * jp = qp->q_head;
    while (jp) {
        if (pthread_equal(jp->j_id, id))
            break; 
        jp= jp->j_next;
    }
    pthread_rwlock_unlock(&qp->q_lock);
    return jp;
}


